package com.xuyu.nacos.callback.listener.configcore;

import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.api.config.ConfigChangeEvent;
import com.alibaba.nacos.api.config.ConfigChangeItem;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.client.config.listener.impl.AbstractConfigChangeListener;
import com.xuyu.nacos.callback.listener.analysekey.KeyFactory;
import com.xuyu.nacos.callback.listener.annoation.NacosListener;
import com.xuyu.nacos.callback.listener.annoation.NacosListenerCallBackMethod;
import com.xuyu.nacos.callback.listener.annoation.OnChangeCallBack;
import com.xuyu.nacos.callback.listener.helper.AnalyseHelper;
import com.xuyu.nacos.callback.listener.helper.RegisterHelper;
import com.xuyu.nacos.callback.listener.model.NacosConfigListenerProperties;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import javax.annotation.PostConstruct;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Consumer;

/**
 * @author xuyu
 * nacos 读取的配置中心
 */
@Component
public class NacosConfig implements InitializingBean {

    private final NacosConfigManager nacosConfigManager;

    private final ApplicationContext applicationContext;

    private final KeyFactory keyFactory;


    @Value(value = "${spring.application.name}")
    private String appName;


    /**
     * 配置变更业务监听容器
     * key为监听的key
     * value为当这个key变动的时候，触发调用的方法，支持多个，顺序调用
     */
    private final Map<String, List<Consumer<ConfigChangeItem>>> changeListener = new HashMap<>();


    /**
     * 初始化nacos配置类
     */
    public NacosConfig(NacosConfigManager nacosConfigManager,
                       ApplicationContext applicationContext,
                       KeyFactory keyFactory) {
        this.nacosConfigManager = nacosConfigManager;
        this.applicationContext = applicationContext;
        this.keyFactory = keyFactory;
    }

    /**
     * 监听器，用于处理某些特殊的配置变更获取其他特殊的配置变更导致的代码逻辑
     */
    @PostConstruct
    private void reloadConfig() throws NacosException {
        //解析所有要注册监听的配置文件
        List<NacosConfigListenerProperties> dataIds = RegisterHelper.getAllNeedListenConfigFile(nacosConfigManager, appName);
        //向nacos中注册，每个配置文件均要注册
        for (NacosConfigListenerProperties each : dataIds) {
            nacosConfigManager.getConfigService().addListener(each.getSimpleDataId(), each.getGroup(), new AbstractConfigChangeListener() {
                @Override
                public void receiveConfigChange(ConfigChangeEvent configChangeEvent) {
                    Collection<ConfigChangeItem> changeItems = configChangeEvent.getChangeItems();
                    if (ObjectUtils.isEmpty(changeItems)) {
                        return;
                    }
                    for (ConfigChangeItem item : changeItems) {
                        if (changeListener.containsKey(item.getKey())) {
                            List<Consumer<ConfigChangeItem>> consumers = changeListener.get(item.getKey());
                            for (Consumer<ConfigChangeItem> each : consumers) {
                                each.accept(item);
                            }
                        }
                    }
                }
            });
        }
    }


    /**
     * 主要加载配置在方法上的
     */
    private void initListenerForAnnotationMethod(Method[] methods, Object object) {
        if (methods.length == 0 || object == null) {
            return;
        }
        for (Method method : methods) {
            if (method.isAnnotationPresent(NacosListenerCallBackMethod.class)) {
                String key = keyFactory.analyse(method);
                addListener(key, (a) -> {
                    try {
                        method.invoke(object, a);
                    } catch (IllegalAccessException | InvocationTargetException e) {
                        throw new RuntimeException(e);
                    }
                });
            }
        }
    }

    private void initListenerForAnnotationField(Field[] fields) {
        for (Field field : fields) {
            try {
                if (field.isAnnotationPresent(OnChangeCallBack.class)) {
                    OnChangeCallBack annotation = field.getAnnotation(OnChangeCallBack.class);
                    //方法所在类
                    Class<?> methodClass = annotation.methodClass();
                    //方法名
                    String method = annotation.method();
                    if (methodClass == null || method == null || Objects.equals("", method)) {
                        continue;
                    }
                    String key = keyFactory.analyse(field);
                    //从spring获取bean对象
                    Object bean = applicationContext.getBean(methodClass);
                    Method trueMethod = bean.getClass().getMethod(method, ConfigChangeItem.class);
                    addListener(key, (a) -> {
                        try {
                            trueMethod.invoke(bean, a);
                        } catch (IllegalAccessException | InvocationTargetException e) {
                            throw new RuntimeException(e);
                        }
                    });
                }
            } catch (Exception e) {
                //添加监听期间出现异常
                e.printStackTrace();
            }
        }
    }

    private void addListener(String key, Consumer<ConfigChangeItem> consumer) {
        if (!changeListener.containsKey(key)) {
            List<Consumer<ConfigChangeItem>> consumers = new ArrayList<>();
            changeListener.put(key, consumers);
        }

        List<Consumer<ConfigChangeItem>> consumers = changeListener.get(key);
        consumers.add(consumer);
    }


    @Override
    public void afterPropertiesSet() throws Exception {
        //这个方法用于扫描解析所有需要注册的相关回调函数方法
        List<Class<?>> list = new ArrayList<>();
        //先从spring获取加了这个注解的类
        Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(NacosListener.class);
        for (Map.Entry<String, Object> bean : beansWithAnnotation.entrySet()) {
            Object value = bean.getValue();
            //获得原始类
            Class<?> aClass = AnalyseHelper.getTarget(value).getClass();
            if (list.contains(aClass)) {
                //防止重复添加
                continue;
            }
            Method[] methods = aClass.getMethods();
            Field[] fields = aClass.getDeclaredFields();
            //加载添加在方法上的
            initListenerForAnnotationMethod(methods, value);
            //加载添加在字段上的
            initListenerForAnnotationField(fields);
            list.add(aClass);
        }
    }
}
